﻿using System;
using System.Collections.Generic;
using System.Exceptions;
using System.Linq;
using System.Text;
using System.Reflection;

namespace Infinity.Types
{
	[Serializable]
	public abstract class SubType
	{
		public static SubType<object> UnionTypes<T, W>(SubType<T> first, SubType<W> second)
		{
			return new SubType<object>(
				(item) =>
					{
						if (item.GetType() == typeof(T))
						{
							return first.Condition((T)item);
						}
						else if (item.GetType() == typeof(W))
						{
							return second.Condition((W)item);
						}
						return false;
					});
		}

		public abstract bool IsElementOf(object obj);

		public abstract Func<object, bool> Condition { get; set; }

		public abstract object Value { get; set; }

		public abstract bool IsUndefined { get; set; }

		public abstract Type BaseType { get; }
	}

	/// <summary>
	/// Megszorításos (feltételes) típusok reprezentációja
	/// </summary>
	/// <typeparam name="T">Megszorítás típusa</typeparam>
	public class SubType<T> : ISubType<T>
	{
		public SubType()
			: this(default(T))
		{
			IsUndefined = true;
		}

		public SubType(Func<T, bool> condition)
			: this()
		{
			Condition = condition;
		}

		public SubType(T value)
			: this(value, (item) => true)
		{
		}

		public SubType(T value, Func<T, bool> condition)
		{
			IsUndefined = false;
			Condition = condition;
			Value = _Value;
		}

		/// <summary>
		/// Visszatér a típus alaptípusával.
		/// </summary>
		public Type GetBaseType()
		{
			return typeof(T);
		}

		/// <summary>
		/// Igazzal tér vissza, ha a paraméterként megadott elem eleme-e a halmaznak
		/// </summary>
		/// <param name="obj"></param>
		/// <returns></returns>
		public bool IsElementOf(T obj)
		{
			return Condition(obj);
		}

		public SubType<T> GetComplement()
		{
			return SubType<T>.ComplementTypes(this);
		}

		/// <summary>
		/// A típushoz tartozó feltétel
		/// </summary>
		public Func<T, bool> Condition { get; protected set; }

		private T _Value;

		public T Value
		{
			get
			{
				if (IsUndefined) throw new UndefinedValueException();
				return _Value;
			}
			set
			{
				if (IsElementOf(value))
				{
					if (IsUndefined) IsUndefined = false;
					_Value = value;
				}
				else
				{
					throw new InvalidCastException();
				}
			}
		}

		public static implicit operator T(SubType<T> value)
		{
			return value.Value;
		}

		/// <summary>
		/// Kapott-e már értéket
		/// </summary>
		public bool IsUndefined { get; set; }

		public static SubType<T> UnionTypes(SubType<T> first, SubType<T> second)
		{
			return new SubType<T>()
				       {
					       Condition = (item) => first.Condition(item) || second.Condition(item)
				       };
		}

		public static SubType<T> IntersectTypes(SubType<T> first, SubType<T> second)
		{
			return new SubType<T>()
				       {
					       Condition = (item) => first.Condition(item) && second.Condition(item)
				       };
		}

		public static SubType<T> SubtractTypes(SubType<T> first, SubType<T> second)
		{
			return new SubType<T>()
				       {
					       Condition = (item) => first.Condition(item) && !second.Condition(item)
				       };
		}

		public static SubType<T> ComplementTypes(SubType<T> lazyType)
		{
			return new SubType<T>()
				       {
					       Condition = (item) => !lazyType.Condition(item)
				       };
		}
	}
}